package com.quectel.emp.controller.devicepatrol;

import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.constant.core.devicepatrol.DevicePatrolEventReportConstants;
import com.quectel.constant.core.devicepatrol.DevicePatrolTaskConstants;
import com.quectel.constant.core.workorder.WorkOrderConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.dept.dto.DeptPersonDto;
import com.quectel.core.module.dept.service.DeptPersonService;
import com.quectel.core.module.devicepatrol.dto.DevicePatrolEventReportDto;
import com.quectel.core.module.devicepatrol.dto.DevicePatrolTaskDto;
import com.quectel.core.module.devicepatrol.service.DevicePatrolEventReportService;
import com.quectel.core.module.devicepatrol.service.DevicePatrolTaskService;
import com.quectel.core.module.workorder.dto.WorkOrderDto;
import com.quectel.emp.controller.BaseController;
import com.quectel.emp.interceptor.LoginEmpSession;
import com.quectel.emp.service.workorder.ApiWorkOrderService;
import com.quectel.emp.util.SessionHolder;
import com.quectel.emp.vo.devicepatrol.DevicePatrolEventReportVo;
import com.quectel.util.common.*;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * @author zhangheng
 * @email nolan.zhang@quectel.com
 */
@RestController
@RequestMapping("devicePatrol")
@Tag(name = "DevicePatrolController", description = "设备巡检相关api")
public class DevicePatrolController extends BaseController {


    @DubboReference
    private DevicePatrolTaskService devicePatrolTaskService;
    @DubboReference
    private DevicePatrolEventReportService devicePatrolEventReportService;
    @DubboReference
    private DeptPersonService deptPersonService;

    @Autowired
    private ApiWorkOrderService apiWorkOrderService;


    /**
     * 巡检设备上报限制,根据taskId+deviceId
     */
    private static final String DEVICE_PATROL_REPORT_LIMIT_LOCK_BY_TASKID_AND_DEVICEID = "DEVICE_PATROL_REPORT_LIMIT_LOCK_BY_TASKID_AND_DEVICEID:";

    /**
     * 设备上报间隔
     */
    private static final int DEVICE_PATROL_REPORT_TIME_LIMIT = 10;

    /**
     * 巡检设备上报锁,根据taskId
     */
    private static final String DEVICE_PATROL_REPORT_LIMIT_LOCK_BY_TASKID = "DEVICE_PATROL_REPORT_LIMIT_LOCK_BY_TASKID:";

    /**
     * 分页查询巡检任务列表
     *
     * @param statusFlag
     * @param pageNo
     * @param pageSize
     * @return
     */
    @GetMapping("/list")
    @Operation(summary = "分页查询巡检任务列表")
    public Response<Page<DevicePatrolTaskDto>> list(
            @Parameter(description = "0未执行1执行中2已执行") @RequestParam Byte statusFlag,
            @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条") @RequestParam Integer pageSize) {

        Integer statusFlagInt = Integer.valueOf(statusFlag);
        if (DevicePatrolTaskConstants.StatusFlagEnum.STATUS_EXECUTED.getStatus() < statusFlagInt) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }
        Request request = Request.configParams(Request.parseParams(httpServletRequest));
        //标记查询来源
        request.getParams().put(DevicePatrolTaskConstants.EMP_QUERY, SystemConstants.YES.toString());
        request.getParams().put(DevicePatrolTaskConstants.EMP_QUERY_USER_ID, String.valueOf(SessionHolder.getEmp().getId()));
        request.getParams().put(DevicePatrolTaskConstants.QUERY_TIME, DateUtils.format(new Date(), DateUtils.FormatType.COMMON));
        request.getParams().put(LambdaUtil.getFieldName(DevicePatrolTaskDto::getVillageId), String.valueOf(SessionHolder.getEmp().getVillageId()));

        if (DevicePatrolTaskConstants.StatusFlagEnum.STATUS_EXECUTING.getStatus().equals(statusFlag)) {
            List<Byte> statuses = new ArrayList<>();
            statuses.add(DevicePatrolTaskConstants.TasksStatusEnum.STATUS_UNEXECUTED.getStatus());
            statuses.add(DevicePatrolTaskConstants.TasksStatusEnum.STATUS_EXECUTING.getStatus());
            request.getParams().put(SystemConstants.STATUSES, statuses);
        }

        if (DevicePatrolTaskConstants.StatusFlagEnum.STATUS_EXECUTED.getStatus().equals(statusFlag)) {
            List<Byte> statuses = new ArrayList<>();
            statuses.add(DevicePatrolTaskConstants.TasksStatusEnum.STATUS_EXECUTED.getStatus());
            statuses.add(DevicePatrolTaskConstants.TasksStatusEnum.STATUS_MISSED_DETECTION.getStatus());
            request.getParams().put(SystemConstants.STATUSES, statuses);
        }

        List<DevicePatrolTaskDto> result = null;
        int total = devicePatrolTaskService.queryTotal(request.getParams());
        if (total > 0) {
            result = devicePatrolTaskService.queryList(request.getParams());
        } else {
            result = new ArrayList<>();
        }

        Page<DevicePatrolTaskDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, result);
        return Response.<Page<DevicePatrolTaskDto>>ok().wrap(page);
    }

    /**
     * 根据id查询巡检任务记录
     */
    @GetMapping("info/{id}")
    @Operation(summary = "根据id查询巡检任务记录")
    public Response<DevicePatrolTaskDto> info(@PathVariable("id") Long id) {

        //登录信息
        LoginEmpSession loginEmpSession = SessionHolder.getLoginEmpSession();

        Map<String, Object> params = new HashMap<>(2);
        params.put(SystemConstants.ENTITY_ID_FLAG, String.valueOf(id));
        params.put(LambdaUtil.getFieldName(DevicePatrolTaskDto::getExecUserId), loginEmpSession.getDeptPersonDto().getId());
        if (devicePatrolTaskService.queryTotal(params) <= 0) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }

        DevicePatrolTaskDto devicePatrolTaskDto = devicePatrolTaskService.selectById(id);

        return Response.<DevicePatrolTaskDto>ok().wrap((devicePatrolTaskDto));
    }

    /**
     * 巡检任务上报
     */
    @PostMapping("report")
    @Operation(summary = "巡检任务上报")
    public Response<Object> report(@Validated @RequestBody DevicePatrolEventReportVo vo) {

        //幂等校验 防止重复提交
        if (!RedisUtils.setnx(DEVICE_PATROL_REPORT_LIMIT_LOCK_BY_TASKID_AND_DEVICEID + vo.getTaskId() + SystemConstants.COLON + vo.getDeviceId(), SystemConstants.YES.toString(), DEVICE_PATROL_REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            throw new CheckErrorException("上报过于频繁");
        }

        DevicePatrolTaskDto devicePatrolTaskDto = devicePatrolTaskService.selectCacheById(vo.getTaskId());

        DeptPersonDto emp = SessionHolder.getEmp();

        //参数校验
        if (devicePatrolTaskDto == null) {
            throw new CheckErrorException("当前任务不存在");
        }
        if (DevicePatrolTaskConstants.TasksStatusEnum.STATUS_EXECUTED.getStatus().equals(devicePatrolTaskDto.getExecStatus())) {
            throw new CheckErrorException("当前任务不可执行");
        }
        if (!devicePatrolTaskDto.getExecUserId().equals(emp.getId())) {
            throw new CheckErrorException("员工不可执行该任务");
        }

        DevicePatrolEventReportDto reportDto = devicePatrolEventReportService.selectByTaskIdAndDeviceId(vo.getTaskId(), vo.getDeviceId());
        //如果查询不到上报记录非法操作
        if (reportDto == null) {
            throw new CheckErrorException(SystemConstants.ILLEGAL_MSG);
        }

        //如果是跳检
        if (DevicePatrolEventReportConstants.ReportStatusEnum.STATUS_SKIP_INSPECTION.getStatus().equals(vo.getStatus())) {
            if (vo.getSkipCheckReasonType() == null) {
                throw new CheckErrorException("跳检原因不能为空");
            }
            if (DevicePatrolEventReportConstants.AssignWorkOrderStatusEnum.ASSIGN_WORK_ORDER.getStatus().equals(vo.getAssignWorkOrder()) || vo.getAssignUserId() != null) {
                throw new CheckErrorException("跳检不能创建工单");
            }
        } else {
            //非跳检需要上传经纬度
            if (StringUtils.isEmpty(vo.getAddress())
                    || vo.getLat() == null || vo.getLng() == null) {
                throw new CheckErrorException("上报地址不能为空");
            }
        }

        if (!DevicePatrolEventReportConstants.ReportStatusEnum.STATUS_UNEXECUTED.getStatus().equals(reportDto.getStatus())) {
            throw new CheckErrorException("不能重复上报");
        }

        //是否在执行时间
        Date now = new Date();
        if (now.before(devicePatrolTaskDto.getExecStartTime()) || now.after(devicePatrolTaskDto.getExecEndTime())) {
            throw new CheckErrorException("当前时间不在该任务的时间段范围之内");
        }
        //图片数组校验 未通过会抛出异常被baseController捕获
        if (StringUtils.isNotBlank(vo.getSnapshotImg())) {
            JacksonUtils.parseArray(vo.getSnapshotImg(), String.class);
        }

        List<DevicePatrolEventReportDto> patrolEventReportDtos = devicePatrolTaskDto.getDevicePatrolEventReportDtos();

        for (int i = 0; i < patrolEventReportDtos.size(); i++) {
            //第一个状态未执行的 必须等于上报设备ID
            if (DevicePatrolEventReportConstants.ReportStatusEnum.STATUS_UNEXECUTED.getStatus().equals(patrolEventReportDtos.get(i).getStatus())) {
                if (!patrolEventReportDtos.get(i).getDeviceId().equals(vo.getDeviceId())) {
                    throw new CheckErrorException(SystemConstants.ILLEGAL_MSG);
                } else {
                    break;
                }
            }
        }

        reportDto.setSnapshotImg(vo.getSnapshotImg());
        reportDto.setDetail(vo.getDetail());
        reportDto.setLng(vo.getLng());
        reportDto.setLat(vo.getLat());
        reportDto.setAddress(vo.getAddress());
        reportDto.setSkipCheckReasonType(vo.getSkipCheckReasonType());
        reportDto.setStatus(vo.getStatus());
        reportDto.setAssignWorkOrder(vo.getAssignWorkOrder());
        reportDto.setAssignUserId(vo.getAssignUserId());
        reportDto.setCompleteTime(now);

        //上报异常才能生成工单
        if (DevicePatrolEventReportConstants.AssignWorkOrderStatusEnum.ASSIGN_WORK_ORDER.getStatus().equals(vo.getAssignWorkOrder())) {
            WorkOrderDto workOrderDto = apiWorkOrderService.patrolReportOrder(emp.getId(), WorkOrderConstants.CategoryAndType.DEVICE_PATROL, vo.getDetail(),
                    vo.getSnapshotImg(), vo.getAssignUserId(), reportDto.getDeviceId());
            reportDto.setWorkOrderId(workOrderDto.getId());
            reportDto.setWorkOrderNo(workOrderDto.getOrderCode());

        }
        Lock spinLock = RedisUtils.getSpinLock(DEVICE_PATROL_REPORT_LIMIT_LOCK_BY_TASKID + devicePatrolTaskDto.getId());
        spinLock.lock();
        try {
            devicePatrolEventReportService.report(reportDto, devicePatrolTaskDto);
        } catch (Exception e) {
            throw new CheckErrorException("上报过于频繁");
        } finally {
            spinLock.unlock();
        }

        return Response.ok().wrap("上报成功");
    }


}
